(in-package :leam)
(declaim (optimize (speed 0)(safety 3)(debug 3)))

(defgeneric read-unsigned-8( stream )
  (:documentation "Read an unsigned byte")
  (:method (stream)
    (read-byte stream)))
(export 'read-unsigned-8)

(defgeneric write-unsigned-8(stream data)
  (:documentation "Write an unsigned bytem")
  (:method (stream data)
    (write-byte data stream)))
(export 'write-unsigned-8)

(defgeneric read-unsigned-16(stream)
  (:documentation "Read an unsigned short")
  (:method (stream)
    (+ (read-unsigned-8 stream) (ash (read-unsigned-8 stream) 8))))
(export 'read-unsigned-16)

(defgeneric write-unsigned-16(stream data)
  (:documentation "Write and unsigned short")
  (:method (stream data)
    (write-unsigned-8 stream (logand data 255))
    (write-unsigned-8 stream (ash data -8))))
(export 'write-unsigned-16)

(defgeneric read-unsigned-32(stream)
  (:documentation "Read an unsigned long")
  (:method (stream)
    (+ (read-unsigned-8 stream)
       (ash (read-unsigned-8 stream) 8)
       (ash (read-unsigned-8 stream) 16)
       (ash (read-unsigned-8 stream) 24))))
(export 'read-unsigned-32)

(defgeneric write-unsigned-32(stream data)
  (:documentation "Write and unsigned long")
  (:method (stream data)
    (write-unsigned-8 stream (logand data 255))
    (write-unsigned-8 stream (logand (ash data -8) 255))
    (write-unsigned-8 stream (logand (ash data -16) 255))
    (write-unsigned-8 stream (logand (ash data -24) 255))))
(export 'write-unsigned-32)

(defgeneric read-char-string(stream seq)
  (:documentation "Read an iso8859-1 8 bit character sequence")
  (:method (stream seq)
    (let ((seq-len (length seq)))
      (loop for index from 0 to (- seq-len 1) do
	    (setf (aref seq index) (code-char (read-unsigned-8 stream)))))))
(export 'read-char-string)

(defgeneric write-char-string(stream sequence)
  (:documentation "Write and iso8859-1 8 bit character sequence")
  (:method (stream sequence)
    (with-input-from-string ( str sequence )
      (do ((item (read-char str nil) (read-char str nil)))
	  ((null item))
	(write-unsigned-8 stream (char-int item))))))
(export 'write-char-string)

(defgeneric read-byte-data(stream seq)
  (:documentation "Read raw 8-bit data.  It is up the the caller to
allocate a sequence of type '(unsigned-byte 8)")
  (:method (stream seq)
    (read-sequence seq stream)))
(export 'read-byte-data)

(defgeneric write-byte-data(stream sequence)
  (:documentation "Write raw 8-bit data.  It is up the the caller to
allocate a sequence of type '(unsigned=byte 8)")
  (:method (stream sequence)
    (write-sequence sequence stream )))
(export 'write-byte-data)

(defgeneric ignore-data(stream len)
  (:documentation "Ignore a length of data")
  (:method (stream len)
    (loop for index from 1 to len do
	  (read-unsigned-8 stream))))
(export 'ignore-data)

(defgeneric leam-finish-output(stream)
  (:documentation "Ensure output is finished")
  (:method (stream)
    (finish-output stream)))

(defgeneric leam-close(stream)
  (:documentation "Message to close the stream.  This is mainly for overridden
streams, this doesn't call the close method as it is up to the user to close
resources passed to the leam API.")
  (:method (stream)))
(export 'leam-close)


(defclass leam-in-memory-io-stream()
  ((vector
    :initform (make-array 0 :adjustable t
			  :fill-pointer 0 :element-type '(unsigned-byte 8)))
   (read-pointer
    :initform 0)))

(export 'leam-in-memory-io-stream)

;;In order to define your own stream, all you need are these four methods.
;;Everything else is defined in terms of these methods.

(defmethod read-unsigned-8( (stream leam-in-memory-io-stream))
  (with-slots
	(vector read-pointer)
      stream
    (when (>= read-pointer (fill-pointer vector))
      (error "Attempt to read past end of stream"))
    (let ((retval (aref vector read-pointer)))
      (setf read-pointer (+ read-pointer 1))
      retval )))

(defmethod write-unsigned-8((stream leam-in-memory-io-stream) data)
  (with-slots (vector)
      stream
    (vector-push-extend data vector)))

(defmethod read-byte-data((stream leam-in-memory-io-stream) sequence)
  (with-slots
	(vector read-pointer)
      stream
    (let ((request_len (length sequence)))
      (loop for index from 0 to (- request_len 1) do
	    (setf (aref sequence index) (aref vector (+ read-pointer index))))
      (setf read-pointer (+ read-pointer request_len)))))

(defmethod write-byte-data((stream leam-in-memory-io-stream) sequence)
  (with-slots
	(vector)
      stream
    (loop for index from 0 to (- (length sequence) 1) do
	  (vector-push-extend (aref sequence index) vector ))))

;ignored
(defmethod leam-finish-output((stream leam-in-memory-io-stream)))



    